/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ipsec_vpn_ctl.h"

#include <string>

#include "base64_utils.h"
#include "netmgr_ext_log_wrapper.h"
#include "netmanager_base_common_utils.h"
#include "net_manager_ext_constants.h"
#include "cJSON.h"

namespace OHOS {
namespace NetManagerStandard {
IpsecVpnCtl::IpsecVpnCtl(sptr<VpnConfig> config, const std::string &pkg, int32_t userId,
    std::vector<int32_t> &activeUserIds)
    : NetVpnImpl(config, pkg, userId, activeUserIds)
{}

IpsecVpnCtl::~IpsecVpnCtl()
{
    NETMGR_EXT_LOG_I("~IpsecVpnCtl");
}

bool IpsecVpnCtl::IsSystemVpn()
{
    return true;
}

int32_t IpsecVpnCtl::SetUp()
{
    return StartSysVpn();
}

int32_t IpsecVpnCtl::Destroy()
{
    StopSysVpn();
    int result = NetVpnImpl::Destroy();
    NETMGR_EXT_LOG_I("openvpn Destroy result %{public}d}", result);
    return result;
}

int32_t IpsecVpnCtl::StopSysVpn()
{
    NETMGR_EXT_LOG_I("stop ipsec vpn");
    state_ = IpsecVpnStateCode::STATE_DISCONNECTED;
    NetsysController::GetInstance().ProcessVpnStage(SysVpnStageCode::VPN_STAGE_DOWN_HOME);
    NetsysController::GetInstance().ProcessVpnStage(SysVpnStageCode::VPN_STAGE_STOP);
    NotifyConnectState(VpnConnectState::VPN_DISCONNECTED);
    return NETMANAGER_EXT_SUCCESS;
}

int32_t IpsecVpnCtl::StartSysVpn()
{
    NETMGR_EXT_LOG_I("start ipsec vpn");
    state_ = IpsecVpnStateCode::STATE_INIT;
    InitConfigFile();
    NetsysController::GetInstance().ProcessVpnStage(SysVpnStageCode::VPN_STAGE_RESTART);
    return NETMANAGER_EXT_SUCCESS;
}

int32_t IpsecVpnCtl::InitConfigFile()
{
    CleanTempFiles();
    if (ipsecVpnConfig_ == nullptr) {
        NETMGR_EXT_LOG_E("InitConfigFile ipsecVpnConfig is null");
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    if (!ipsecVpnConfig_->strongswanConf_.empty()) {
        std::string strongswanCfg = Base64::Decode(ipsecVpnConfig_->strongswanConf_);
        if (!strongswanCfg.empty()) {
            CommonUtils::WriteFile(SWAN_CONFIG_FILE, strongswanCfg);
        }
    }
    return NETMANAGER_EXT_SUCCESS;
}

void IpsecVpnCtl::CleanTempFiles()
{
    DeleteTempFile(SWAN_CONFIG_FILE);
    DeleteTempFile(L2TP_CFG);
    DeleteTempFile(L2TP_IPSEC_CFG);
}

void IpsecVpnCtl::DeleteTempFile(const std::string &fileName)
{
    if (std::filesystem::exists(fileName)) {
        if (!std::filesystem::remove(fileName)) {
            NETMGR_EXT_LOG_E("remove old cache file failed");
        }
    }
}

int32_t IpsecVpnCtl::SetUpVpnTun()
{
    int result = NetVpnImpl::SetUp();
    if (result != NETMANAGER_EXT_SUCCESS) {
        NETMGR_EXT_LOG_W("ipsec SetUp failed");
        StopSysVpn();
    }
    NETMGR_EXT_LOG_I("ipsec SetUp %{public}d", result);
    return result;
}

int32_t IpsecVpnCtl::UpdateConfig(const std::string &msg)
{
    if (vpnConfig_ == nullptr) {
        NETMGR_EXT_LOG_E("UpdateConfig vpnConfig_ is null");
        return NETMANAGER_EXT_ERR_PARAMETER_ERROR;
    }
    const char *ret = strstr(msg.c_str(), "{");
    if (ret == nullptr) {
        NETMGR_EXT_LOG_E("client rootJson format error");
        return NETMANAGER_EXT_ERR_PARAMETER_ERROR;
    }
    cJSON* rootJson = cJSON_Parse(ret);
    if (rootJson == nullptr) {
        NETMGR_EXT_LOG_E("not json string");
        return NETMANAGER_EXT_ERR_PARAMETER_ERROR;
    }

    cJSON* jConfig = cJSON_GetObjectItem(rootJson, IPSEC_NODE_UPDATE_CONFIG);
    if (!cJSON_IsObject(jConfig)) {
        cJSON_Delete(rootJson);
        NETMGR_EXT_LOG_E("jConfig format error");
        return NETMANAGER_EXT_ERR_PARAMETER_ERROR;
    }
    cJSON *mtu = cJSON_GetObjectItem(jConfig, IPSEC_NODE_MTU);
    if (mtu != nullptr && cJSON_IsNumber(mtu)) {
        int32_t ipsecVpnMtu = static_cast<int32_t>(cJSON_GetNumberValue(mtu));
        vpnConfig_->mtu_ = ipsecVpnMtu;
        NETMGR_EXT_LOG_I("UpdateConfig mtu %{public}d", ipsecVpnMtu);
    }

    INetAddr iNetAddr;
    INetAddr destination;
    INetAddr gateway;
    Route iRoute;
    cJSON *address = cJSON_GetObjectItem(jConfig, IPSEC_NODE_ADDRESS);
    if (address != nullptr && cJSON_IsString(address)) {
        std::string ipsecVpnAddress = cJSON_GetStringValue(address);
        iNetAddr.address_ = ipsecVpnAddress;
        gateway.address_ = ipsecVpnAddress;
        destination.address_ = ipsecVpnAddress;
    }

    cJSON *netmask = cJSON_GetObjectItem(jConfig, IPSEC_NODE_NETMASK);
    if (netmask != nullptr && cJSON_IsString(netmask)) {
        std::string ipsecVpnNetmask = cJSON_GetStringValue(netmask);
        iNetAddr.netMask_ = ipsecVpnNetmask;
        destination.prefixlen_ = CommonUtils::GetMaskLength(ipsecVpnNetmask);
        NETMGR_EXT_LOG_I("UpdateConfig prefixlen %{public}d", destination.prefixlen_);
    }

    cJSON *ifIdJson = cJSON_GetObjectItem(jConfig, IPSEC_NODE_IFID);
    if (ifIdJson != nullptr && cJSON_IsNumber(ifIdJson)) {
        int32_t ifId = static_cast<int32_t>(cJSON_GetNumberValue(ifIdJson));
        //iRoute.ifFd_ = ifId;
        NETMGR_EXT_LOG_I("UpdateConfig ifId %{public}d", ifId);
    }
    cJSON_Delete(rootJson);

    vpnConfig_->addresses_.emplace_back(iNetAddr);
    iRoute.iface_ = "xfrm1";
    iRoute.isDefaultRoute_ = true;
    iRoute.destination_ = destination;
    iRoute.gateway_ = gateway;
    vpnConfig_->routes_.emplace_back(iRoute);
    return NETMANAGER_EXT_SUCCESS;
}


int32_t IpsecVpnCtl::NotifyConnectStage(const std::string &stage, const int32_t &result)
{
    NETMGR_EXT_LOG_E("NotifyConnectStage stage:%{public}s,state_:%{public}d", stage.c_str(), state_);
    if (stage.empty()) {
        NETMGR_EXT_LOG_E("stage is empty");
        return NETMANAGER_EXT_ERR_PARAMETER_ERROR;
    }
    if (result != NETMANAGER_EXT_SUCCESS) {
        NETMGR_EXT_LOG_E("vpn stage: %{public}s failed, result: %{public}d", stage.c_str(), result);
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    switch (state_) {
        case IpsecVpnStateCode::STATE_INIT:
            if (stage.compare(IPSEC_START_TAG) == 0) {
                // 1. start strongswan
                NETMGR_EXT_LOG_I("ipsec vpn setup step 1: start strongswan");
                state_ = IpsecVpnStateCode::STATE_STARTED;
                NetsysController::GetInstance().ProcessVpnStage(SysVpnStageCode::VPN_STAGE_SWANCTL_LOAD);
            }
            break;
        case IpsecVpnStateCode::STATE_STARTED:
            if (stage.compare(SWANCTL_START_TAG) == 0) {
                // 2. start connect
                NETMGR_EXT_LOG_I("ipsec vpn setup step 2: start connect");
                state_ = IpsecVpnStateCode::STATE_CONFIGED;
                NetsysController::GetInstance().ProcessVpnStage(SysVpnStageCode::VPN_STAGE_UP_HOME);
            }
            break;
        case IpsecVpnStateCode::STATE_CONFIGED:
            if (stage.compare(IPSEC_CONNECT_TAG) == 0) {
                // 3. is connected
                NETMGR_EXT_LOG_I("ipsec vpn setup step 3: is connected");
                state_ = IpsecVpnStateCode::STATE_CONNECTED;
                NotifyConnectState(VpnConnectState::VPN_CONNECTED);
                int32_t ret = SetUpVpnTun();
                if (ret != NETMANAGER_EXT_SUCCESS) {
                    NETMGR_EXT_LOG_I("SetUpVpnTun failed");
                    return NETMANAGER_EXT_ERR_INTERNAL;
                }
            } else {
                NETMGR_EXT_LOG_I("start update config");
                if (UpdateConfig(stage) != NETMANAGER_EXT_SUCCESS) {
                    NETMGR_EXT_LOG_I("ipsec vpn config update failed");
                    return NETMANAGER_EXT_ERR_INTERNAL;
                }
            }
            break;
        default:
            NETMGR_EXT_LOG_E("invalid state: %{public}d", state_);
            return NETMANAGER_EXT_ERR_INTERNAL;
    }
    return NETMANAGER_EXT_SUCCESS;
}

int32_t IpsecVpnCtl::GetSysVpnCertUri(const int32_t certType, std::string &certUri)
{
    if (ipsecVpnConfig_ == nullptr) {
        NETMGR_EXT_LOG_E("GetSysVpnCertUri ipsecVpnConfig is null");
        return NETMANAGER_EXT_ERR_INTERNAL;
    }
    switch (certType) {
        case IpsecVpnCertType::CA_CERT:
            certUri = ipsecVpnConfig_->ipsecCaCertConf_;
            break;
        case IpsecVpnCertType::USER_CERT:
            certUri = ipsecVpnConfig_->ipsecPublicUserCertConf_;
            break;
        case IpsecVpnCertType::SERVER_CERT:
            certUri = ipsecVpnConfig_->ipsecPublicServerCertConf_;
            break;
        case IpsecVpnCertType::SWAN_CTL_CONF:
            certUri = Base64::Decode(ipsecVpnConfig_->swanctlConf_);
            break;
        default:
            NETMGR_EXT_LOG_E("invalid certType: %{public}d", certType);
            break;
    }
    return NETMANAGER_EXT_SUCCESS;
}

int32_t IpsecVpnCtl::GetConnectedSysVpnConfig(sptr<SysVpnConfig> &sysVpnConfig)
{
    if (state_ == IpsecVpnStateCode::STATE_CONNECTED && ipsecVpnConfig_ != nullptr) {
        NETMGR_EXT_LOG_I("GetConnectedSysVpnConfig success");
        sysVpnConfig = ipsecVpnConfig_;
    }
    return NETMANAGER_EXT_SUCCESS;
}

bool IpsecVpnCtl::IsInternalVpn()
{
    return true;
}
} // namespace NetManagerStandard
} // namespace OHOS